Így készült:

Zombie Battle

 

 

 

Erdős Zoltán

Érd

2023.04.09

 

 

A játék letölthető innen:

https://ezszoftver.hu/hu/ZombieBattle.html

 

 

Ezzel a leírással be szeretném mutatni, hogy „Unity”-ben hogyan készült el a „Zombie Battle” játék.

Tanulási célból készült ez a leírás. Ha valakit érdekel egy FPS játék elkészítése „Unity”-ben, de nem tudja hogy hogyan kezdjen neki, neki hasznos lehet ez a leírás.

Képernyőképekkel mutatom be a játékban lévő objektumokat, C# forráskóddal.

Sajnos a “ZombieBattle” játék “Unity projektet” nem oszthatom meg veled, mert abban vannak fizetős tartalmak is, de ez a leírás is segíthet.

Jó játékot! 😊

 

 

 

1.  Pálya:

Feladata:

A pályán játszódik a játék. Itt vannak a Zombik, Tudósok, lőszerek és életek.

 

Megvalósítás:

Rendelkezik „MeshCollider”-el, ami az ütközésért felelős. A pályán lévő objektumok nagyrészt „static”-kusok. Láthatatlan „BoxCollider”-el is rendelkelkezik, hogy ne tudjon az „Avatar” kiugrani a pályáról.

 

 

 

2.  Helikopter:

Feladata:

A helikopter mellett kezdődik a játék, és itt fejeződik be. Ha megtalálta az Avatar a doktorokat, akkor a helikopterhez kell mennie az Avatarnak és a doktoroknak, mert akkor menekültek meg a zombik elől.

Fel- le mozog a helikopter.

 

Megvalósítás:

Flying.cs: fel- le mozgás.

Hangot játszik le a helikopter.

Az „Animator” komponens felelős a propeller forgásáért.

 

 

 

3.  Avatar:

Feladata:

Az avatar tud mozogni a pályán, lőni, ugrani tud, újra tölti a fegyvert.

Feladata, hogy megkeresse a doktorokat, majd a helikopterhez kísérje őket, így menti meg őket a zombik elől.

Ha egy zombi támad, azt meg kell ölni, hogy ne sebezzen meg.

Tud életet és lőszert felvenni.

Tud hordót lőni, ami sebzi a zombikat.

 

 

Megvalósítás:

CapsuleCollider: ütközés miatt kell.

 

 

4.  Avatar Canvas:

Feladata:

Megjeleníti a képernyőn a Canvas az avatar életét, a lőszer mennyiségét, a megmentett doktorok számát.

Megjeleníti, hogy milyen messze található a legközelebbi doktor és helikopter.

 

 

 

5.  Avatar Zseblámpa:

Feladata:

Ha az avatar egy sötét részen van, akkor a lámpával meg lehet világítani az előtte lévő teret.

 

 

 

6.  Zombi:

Feladata:

Kóborol a zombi, de ha rá lő az avatar, vagy meglátja az avatar-t, akkor az avatar felé kezd futni.
Útvonalon fut a zombi, vagyis ha akadály van előtte, akkor azt képes kikerülni.

Ha közel ér a zombi az avatar-hoz, akkor ütni kezdi az avatar-t, akinek az élete csökken ilyenkor.

Van a zombi-nak élete, ami akkor csökken, ha az avatar lövi, vagy hordó robban fel a közelében. Ha elfogy a zombi élete, akkor meghal a zombie.

 

Megvalósítás:

CapsuleCollider: ütközésért felel.

NavMeshAgent: ez a komponens tud mozogni az úton, így tud kikerülni akadályokat.

Zombie.cs: a zombi gondolkodásáért felel.

 

 

 

7.  Zombi Kezelő:

Feladata:

Ha túl messze van egy zombi az avatartól, akkor ez a GameObject „disabled” lesz, különben „enabled”. Ez a távolság 60 méter. Így kevesebb cpu-t használ a játék.

 

Megvalósítás:

ZombiesManager.cs

 

 

 

8.  Hordó:

Feladata:

Ha az avatar rá lő a hordóra, akkor a hordó felrobban.

Ha a robbanás közelében van egy másik hordó, akkor az kb. 0,5-1 másodperc múlva felrobban.

A robbanás levonja a zombi és avatar életét.

 

Megvalósítás:

CapsuleCollider: Ütközésért felel.

SphereCollider: Egy gömb, amiben ha benne van az avatar vagy zombi, akkor arról értesül az Explosion.cs

Explosion.cs

 

 

 

9.  Hordó Kezelő:

Feladata:

Ha a hordó túl messze van az avatartól, akkor a GameObject „disabled”-re állítódik, különben „enabled”. Így kevesebbet kell számolnia a cpu-nak. Ez most 60méter.

 

Megvalósítás:

GasBallonesManager.cs

 

 

 

10.      Élet:

Feladata:

Ez ad életet az avatarnak, ha az avatar ütözik ezzel.

 

Megvalósítás:

SphereCollider: Ütözés figyelő.

HealUp.cs

 

 

 

11.      Lőszer:

Feladata:

Ez ad lőszert az avatarnak, ha az ütközik ezzel.

 

Megvalósítás:

SphereCollider: Ütközés figyelő.

WeaponUp.cs

 

 

 

12.      Háttérzene:

Feladata:

A háttérzene lejátszást végzi. A zene ismétlődik.

 

 

 

13.      Doktor:

Feladata:

Őket kell megmentenie az avatarnak.
Egy helybe állnak, néha „help”-et kiabálnak.

Ha az avatar melléjük érkezik, akkor megjelenik egy „HealUp” a doktor mellett, amit felvehet az avatar.
Majd a doktor elkezdi követni az avatart. Ha akadály van a doktor és avatar között, akkor azt kikerüli a doktor.

 

Megvalósítás:

CapsuleCollider: az ütközésért felel.

NavMeshAgent: ez a komponens felel azért hogy a doktor tudjon útvonalon közlekedni, így kikerülve az akadályokat.

Doctor.cs

 

 

 

14.      Doktor Kezelő:

Feladata:

Ha az összes doktor a helikopter alatt van, akkor át lett véve a pálya.

 

Megvalósítás:

DoctorsManager.cs

 

 

 

15.      Pause menü:

Feladata:

Szüneteltetni lehet a játékot, vagy ki lehet lépni a főmenübe.

 

 

 

16.      Fő menü:

Feladata:

Ez az első felület, ami a játék indítása után fogadja a felhasználót.

„Új játékot” lehet kezdeni.

Folytatni lehet a játékot, így nem kell ellőről kezdeni azt.

A Beállításokba lehet lépni.

Információt lehet olvasni.

Ki lehet lépni a játékból.

 

 

 

17.      Fő menü (DontDestroy Object):

Feladata:

A DontDestroy GameObject végig a memóriában marad, nem törlődik ha egy másik .scene kerül betöltésre.

Ez tálolja a játék állását.

 

Megvalósítás:

DontDestroy.cs

 

 

 

18.      Beállítások menü:

Feladata:

Itt vannak a beállítások.

Felbontást lehet változtatni.

Lehet váltani teljes képernyő/ablak mód között.

Hangerőt lehet állítani.

 

 

 

19.      Infó menü:

Feladata:

Információt kap a felhasználó az avatar irányításáról és a játékról.

 

 

 

20.      Történet panel:

Feladata:

Amikor új játékot kezd a felhasználó, akkor egy rövid történet fogadja őt.

Kattints a „Start” button-ra.

 

 

 

21.      Success menü:

Feladata:

Ha átvitt egy pályát a játékos, akkor ez a képernyő fogadja.

Tovább tud lépni a következő pályára.

Mentésre kerül a játék, így később tudja folytatni ettől a ponttól a felhasználó.

 

 

 

22.      GameOver menü:

Feladata:

Ha elfogy az élete az avatar-nak, akkor a „GameOver” képernyő fogadja a játékost.

Vissza tud lépni a főmenübe.

 

 

 

23.      Congratulations menü:

Feladata:

Ha átvitte a pályákat a játékos, akkor ez a képernyő fogad.

Játék vége szöveg és információ a fejlesztőről.

Vissza lehet lépni a főmenü-be.

 

 

 

24.      Post Processing:

Feladata:

Effekt-et lehet a képre tenni.

Az Ambient Occlusion effekt besötétíti a sarkokat.

Az élsimítás effekt az éleket elmossa.

 

Megvalósítás:

„Post Process Volume” és „Post-Process Layer” komponensek.

 

 

 

25.      Occlusion Culling:

Feladata:

Azokat az objektumokat nem rajzolja ki a Unity, amik a képernyőn takarásban vannak.

 

Megvalósítás:

Kattints az „Window -> Rendering -> Occlusion” menüre, és a megjelenő panelen kattints a „Bake” button-ra.

 

 

 

26.      Baked Lightmaps:

Feladata:

Azok a GameObject-ek amik statikus-ak (mozdulatlanok), azokra ki lehet számolni a rá érkező fényeket, árnyékokat, amik szebbek, mint amit a Unity valós időben számol fényeket, árnyékokat.

Így szebb lesz a pálya kinézete.

 

Megvalósítás:

Kattints a „Window -> Rendering -> Lighting” menüre, állísd be a paramétereket, majd kattints a „Generate Lighting” buttonra.

 

 

 

27.      Build Game:

Feladata:

Le lehet menteni a játékot Windows-ra, Linux-ra, és MacOS-re. Így a játék futtatásához nincs szükség a Unity játékszerkesztőre.

A textúrák maximális méretét is be lehet itt állítani.

 

Megvalósítás:

Kattints a „File -> Bulid” menüre, válaszd ki a legördülő menüben az Operációs rendszert, majd kattints a „Build” buttonra.

 

 

 

 

 

Forráskódok:

 

Flying.cs:

public class Flying : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        m_v3Base = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z); // helikopter pozíció eltárolása

    }

 

    // Update is called once per frame

    void Update()

    {

        m_fTime += Time.deltaTime; // idő növelése

 

        float y = Mathf.Sin((2.0f * Mathf.PI) * m_fTime * speed) * amplitude; // hullámzás kiszámítása

        Vector3 v3NewPosition = m_v3Base + new Vector3(0, y, 0);

 

        this.transform.position = v3NewPosition; // hullámzás alkalmazása

    }

 

    Vector3 m_v3Base;

    float speed = 0.5f;

    float amplitude = 0.1f;

    float m_fTime = 0.0f;

}

 

 

Zombie.cs:

public class Zombie : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        animator = GetComponent<Animator>(); // komponensek lekérdewzése, inicializálás

        //movement = GetComponent<CharacterController>();

        rigidBody = GetComponent<Rigidbody>();

        m_navMeshAgent = GetComponent<NavMeshAgent>();

        m_soundIdle = Instantiate(m_soundIdle, this.transform);

        m_soundAttack = Instantiate(m_soundAttack, this.transform);

        m_soundDie = Instantiate(m_soundDie, this.transform);

        v3Dir = this.transform.forward;

        state = State.Walk;

        m_nElapsedLife = m_nLife;

    }

 

    // Update is called once per frame

    void FixedUpdate()

    {

        m_soundIdle.transform.position = this.transform.position; // hangok pozíciójának beállítása

        m_soundAttack.transform.position = this.transform.position;

        m_soundDie.transform.position = this.transform.position;

 

        // die

        if (m_nLife <= 0.0f) // ha az élet negatív, akkor meghal a zombi

        {

            if (false == m_soundDie.GetComponent<AudioSource>().isPlaying && true == m_bIsFirstDie) // hang lejátszása

            {

                m_bIsFirstDie = false;

                m_soundDie.GetComponent<AudioSource>().Play();

 

                //m_ZombieCounter.Add();

            }

 

            state = State.Die;

            animator.SetBool("walk", false);

            animator.SetBool("run", false);

            animator.SetBool("attack", false);

            animator.SetBool("die", true); // halál animáció indítása

 

            Destroy(GetComponent<Rigidbody>(), 0.0f); // komponensek törlése

            Destroy(GetComponent<NavMeshAgent>(), 0.0f);

            Destroy(GetComponent<CapsuleCollider>(), 0.0f);

 

            return;

        }

 

        if (true == SearchAvatar()) // megtalálta az avatar-t?

        {

            if ((m_objAvatar.transform.position - this.transform.position).magnitude < 1.45f) // ha közel van hozzá, akkor támadás animáció

            {

                state = State.Attack;

                animator.SetBool("walk", false);

                animator.SetBool("run", false);

                animator.SetBool("die", false);

                animator.SetBool("attack", true);

            }

            else // ha távol van tőle akkor futás animáció

            {

                state = State.Run;

                animator.SetBool("walk", false);

                animator.SetBool("attack", false);

                animator.SetBool("die", false);

                animator.SetBool("run", true);

            }

        }

        else if (LeaveAvatar()) // elhagyta az avatart?

        {

            state = State.Walk; // sétálás animáció

            animator.SetBool("run", false);

            animator.SetBool("attack", false);

            animator.SetBool("die", false);

            animator.SetBool("walk", true);

        }

 

        // idle

        switch (state)

        {

            case (State.Walk): // ha sétál

                {

                    if (null == sleep) // várakozás 0.1 .. 3.0 másodpercig

                    {

                        float fSleep = Random.Range(0.1f, 3.0f);

 

                        sleep = Instantiate(m_objSleep);

                        sleep.GetComponent<Sleep>().Begin(fSleep);

 

                        fYawSpeed = Random.Range(-90.0f, 90.0f);

                    }

 

                    if (true == sleep?.GetComponent<Sleep>().IsEnd()) // ha lejárt a várakozás

                    {

                        Destroy(sleep);

                        sleep = null;

 

                        // new yaw

                        fYawSpeed = Random.Range(-90.0f, 90.0f); // zombi forgás szögsebesség

 

                        // sound

                        if (false == m_soundIdle.GetComponent<AudioSource>().isPlaying) // hang lejátszás

                        {

                            m_soundIdle.GetComponent<AudioSource>().Play();

                        }

                    }

 

                    v3Dir = Quaternion.Euler(0, fYawSpeed * Time.deltaTime, 0) * v3Dir; // forgás kiszámítása

                    v3Dir.Normalize();

 

                    Vector3 v3TargetPos = this.transform.position + v3Dir;

 

                    Vector3 v3Step = (v3Dir * m_fWalkVelocity) * Time.deltaTime;

                    //movement.enabled = true;

                    //movement.Move(v3Step);

                    rigidBody.isKinematic = true;

                    this.transform.position += v3Step;

 

                    this.transform.LookAt(v3TargetPos, new Vector3(0, 1, 0)); // zombi forogjon

 

                    break;

                }

            case (State.Run): // ha fut a zombi

                {

                    //movement.enabled = false;

                    if (true == m_navMeshAgent?.SetDestination(m_objAvatar.transform.position)) // avatarhoz, útvonal keresése

                    {

                        rigidBody.isKinematic = true;

                    }

                    else

                    {

                        rigidBody.isKinematic = false;

                    }

 

                    // sound

                    if (false == m_soundAttack.GetComponent<AudioSource>().isPlaying) // hang lejátszás

                    {

                        m_soundAttack.GetComponent<AudioSource>().Play();

                    }

                    break;

                }

            case (State.Attack): // ha támad a zombi

                {

                    //movement.enabled = false;

                    if (true == m_navMeshAgent?.SetDestination(m_objAvatar.transform.position)) // avatarhoz, útvonal keresése

                    {

                        rigidBody.isKinematic = true;

                    }

                    else

                    {

                        rigidBody.isKinematic = false;

                    }

 

                    m_objAvatar.GetComponent<PlayerMovementScript>().OnAttack(); // levonni az avatar életéből

 

                    // sound

                    if (false == m_soundAttack.GetComponent<AudioSource>().isPlaying)// hang lejátszás

                    {

                        m_soundAttack.GetComponent<AudioSource>().Play();

                    }

 

                    break;

                }

        }

    }

 

    bool SearchAvatar() // avatar keresése

    {

        Vector3 v3Dir1 = this.transform.forward;

        Vector3 v3Dir2 = (m_objAvatar.transform.position - this.transform.position).normalized;

        float fDistance = (m_objAvatar.transform.position - this.transform.position).magnitude;

   

        if (Vector3.Angle(v3Dir1, v3Dir2) <= 88.0f && fDistance <= 15.0f) // ha az avatar a zombi előtt van, és közelebb van mint 15 méter, akkor megtalálta az avatart

        {

            return true;

        }

 

        // avatar hit the zombie

        if (m_nElapsedLife != m_nLife)

        {

            return true;

        }

 

        return false; // nem találta meg az avatart.

    }

 

    bool LeaveAvatar() // elhagyta az avatart?

    {

        float fDistance = (m_objAvatar.transform.position - this.transform.position).magnitude;

 

        if (fDistance > 20.0f) // ha a távolság az avatartól nagyobb mint 20 méter, akkor elhagyta.

        {

            m_nElapsedLife = m_nLife; // reset

            return true;

        }

 

        return false; // nem hagyta el

    }

 

    public void Damage() // zombi életéből levonni 10-et

    {

        m_nLife -= 10;

        if (m_nLife < 0)

        {

            m_nLife = 0;

        }

    }

 

    public void Damage(int nValue) // zombi életéből levonni

    {

        m_nLife -= nValue;

        if (m_nLife < 0)

        {

            m_nLife = 0;

        }

    }

 

    GameObject sleep = null;

    Animator animator = null;

    //CharacterController movement = null;

    Rigidbody rigidBody = null;

    NavMeshAgent m_navMeshAgent = null;

 

    enum State { Walk, Run, Attack, Die };

    State state = State.Walk;

    public float m_fWalkVelocity = 0.5f;

    public float m_fRunVelocity = 3.0f;

    Vector3 v3Dir;

    float fYawSpeed = 0.0f;

    int m_nElapsedLife = 0;

    bool m_bIsFirstDie = true;

 

    [SerializeField] GameObject m_objSleep = null;

    [SerializeField] GameObject m_objAvatar = null;

    public int m_nLife = 100;

 

    [SerializeField] GameObject m_soundIdle = null;

    [SerializeField] GameObject m_soundAttack = null;

    [SerializeField] GameObject m_soundDie = null;

    [SerializeField] KilledZombieCounter m_ZombieCounter;

}

 

 

ZombiesManager.cs:

public class ZombiesManager : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        arrZombies = this.GetComponentsInChildren<Zombie>(); // zombi gameobject-ek lekérése

    }

 

    // Update is called once per frame

    void Update()

    {

        Vector3 pos1 = m_Avatar.transform.position; // avatar pozíciója

 

        foreach (Zombie zombie in arrZombies)

        {

            if (zombie.m_nLife <= 0) { continue; }

 

            Vector3 pos2 = zombie.gameObject.transform.position; // aktuális zombi pozíciója

 

            if (Vector3.Distance(pos1, pos2) > m_fHideDistance) // ha nagyon mewssze van a zombi az avatartól, akkor a zombi gameobject disabled-re állítása. Így nem terheli a cpu-t a sok gameobject

            {

                zombie.gameObject.SetActive(false);

            }

            else

            {

                zombie.gameObject.SetActive(true);

            }

        }

    }

 

    Zombie[] arrZombies = null;

    [SerializeField] GameObject m_Avatar;

    [SerializeField] float m_fHideDistance = 75.0f;

}

 

 

 

Explosion.cs:

public class Explosion : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

       

    }

 

    // Update is called once per frame

    void Update()

    {

        m_listGameObjects.Clear();

    }

 

    private void LateUpdate() // az Update() függvény után hívódik meg automatikusan ez a függvény

    {

        if (m_nLife <= 0) // ha a hordó élete negatív, akkor robbanás

        {

            StartExplosion();

        }

    }

 

    public void StartExplosion(float fTime) // robbanás függvény meghívása x másodperc múlva

    {

        Invoke("StartExplosion", fTime);

    }

 

    public List<GameObject> m_listGameObjects = new List<GameObject>(); // a hordó közelében lévő gameobject-ek

 

    public void StartExplosion()

    {

        if (true == m_bIsFirstExplosion) // ha még nem volt robbanás

        {

           

            m_bIsFirstExplosion = false;

            m_nLife = 0;

 

            // particle system

            GameObject explosion = Instantiate<GameObject>(m_objExplosion, this.transform.position, this.transform.rotation); // robbanás változó létre hozása

            explosion.GetComponent<ParticleSystem>().Play();

            Destroy(explosion, 10.0f);

           

            // sound

            GameObject sound = Instantiate<GameObject>(m_objSound, this.transform.position, this.transform.rotation); // hang változó létre hozása

            sound.GetComponent<AudioSource>().Play();

            Destroy(sound, 5.0f);

 

            // explosion force

            Rigidbody rigidbody = GetComponent<Rigidbody>();

            rigidbody.AddExplosionForce(10000.0f, transform.position + new Vector3(0.0f, 0.4f, 0.0f), 5.0f, -10.0f, ForceMode.Impulse); // robbanás erő alkalmazása

 

            // light

            GetComponentInChildren<Light>().enabled = true; // robbanás fény bekapcsolása

 

            // hide

            Destroy(transform.gameObject, 0.25f); // 0.25 másodperc múlva a hordó törlődik

 

            // damage

            foreach (GameObject obj in m_listGameObjects)

            {

                if (null == obj) { continue; }

 

                // zombie

                Zombie zombie = obj.GetComponent<Zombie>();

                if (null != zombie) // ha zombit talált a hordó mellett, akkor levonni az életéből

                {

                    zombie.Damage(100);

                }

 

                // avatar

                PlayerMovementScript movement = obj.GetComponent<PlayerMovementScript>(); // ha avatart talált a zombi mellett, akkor levonni az életéből

                if (null != movement)

                {

                    movement.AddLife(-75.0f);

                }

            }

        }

    }

 

    void OnTriggerStay(Collider other)// automatikusan meghívódik ez a függvény, ha a hordó triggerjében valamilyen gameobject benne van

    {

        if (null != other.gameObject)

        {

            m_listGameObjects.Add(other.gameObject); // eltárolni a gameobject-et

        }

 

        if (false == m_bIsFirstExplosion) // ha az aktuális hordó még nem robbant fel

        {

            Explosion explosion = other.gameObject.GetComponent<Explosion>(); // és robbanás van a közelben

 

            if (null != explosion) // akkor az aktuális hordó is robbanjon fel 0.75 ,, 1.75 másodperc múlva.

            {

                explosion.StartExplosion(Random.Range(0.75f, 1.75f));

            }

        }

    }

 

    public float m_nLife = 3;

    public GameObject m_objExplosion = null;

    private bool m_bIsFirstExplosion = true;

    public GameObject m_objSound = null;

}

 

 

 

GasBallonesManager.cs:

public class GasBallonesManager : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        arrExplosions = this.GetComponentsInChildren<Explosion>(); // hordók megkeresése

    }

 

    // Update is called once per frame

    void Update()

    {

        Vector3 pos1 = m_Avatar.transform.position; // avatar pozíciója

 

        foreach (Explosion explosion in arrExplosions)

        {

            if (explosion.m_nLife <= 0) { continue; }

 

            Vector3 pos2 = explosion.gameObject.transform.position; // hordó pozíciója

 

            if (Vector3.Distance(pos1, pos2) > m_fHideDistance) // ha a hordó túl messze van az avatartól, akkor disabled-re állítani a gameobject-et. Így kevesebb cpu-t használ a játék.

            {

                explosion.gameObject.SetActive(false);

            }

            else

            {

                explosion.gameObject.SetActive(true); // különben enabled.

            }

        }

    }

 

    Explosion[] arrExplosions = null;

    [SerializeField] GameObject m_Avatar;

    [SerializeField] float m_fHideDistance = 75.0f;

}

 

 

 

HealUp.cs:

public class HealUp : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

    }

 

    // Update is called once per frame

    void Update()

    {

        m_fTime -= Time.deltaTime; // respawn time csökkentése

 

        if (m_fTime <= 0.0f) // ha a respawn time negatív, akkor legyen újra aktív a healup

        {

            m_objDraw.SetActive(true);

        }

        else

        {

            m_objDraw.SetActive(false);

        }

    }

 

    private void OnTriggerEnter(Collider other) // ha ütközik a healup-al valaki, ...

    {

        if (m_fTime > 0.0f)

        {

            return;

        }

 

        if (other.tag == "Player") // ha az avatar ütközik a helup-al

        {

            m_objSound.GetComponent<AudioSource>().Play();// zene lejátszás

            m_Avatar.OnCollision_LifeUp(); // avatar élet növelése

            m_fTime = m_fRespawnTime; // respawn time legyen pozitív

        }

    }

 

    [SerializeField] GameObject m_objDraw = null;

    [SerializeField] GameObject m_objSound = null;

    [SerializeField] PlayerMovementScript m_Avatar = null;

    [SerializeField] float m_fRespawnTime = 30.0f;

 

    float m_fTime = 0.0f;

}

 

 

 

WeaponUp.cs:

public class WeaponUp : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

       

    }

 

    // Update is called once per frame

    void Update()

    {

        m_fTime -= Time.deltaTime; // respawn time csökkentése

 

        if (m_fTime <= 0.0f) // ha eltelt 30 másodperc, akkor újra aktív lesz ez a lőszer.

        {

            m_objDraw.SetActive(true);

        }

        else

        {

            m_objDraw.SetActive(false);

        }

    }

 

    private void OnTriggerEnter(Collider other) // ha a tölténnyel ütközik egy gameobject

    {

        if (m_fTime > 0.0f)

        {

            return;

        }

 

        if (other.tag == "Player") // és az avatar, akkor

        {

            m_objSound.GetComponent<AudioSource>().Play(); // hang lejátszása

            m_Avatar.OnCollision_WeaponUp(); // avatar lőszerének növelése

            m_fTime = m_fRespawnTime; // respawn time legyen 30másodperc

        }

    }

 

    [SerializeField] GameObject m_objDraw = null;

    [SerializeField] GameObject m_objSound = null;

    [SerializeField] PlayerMovementScript m_Avatar = null;

    [SerializeField] float m_fRespawnTime = 30.0f;

 

    float m_fTime = 0.0f;

}

 

 

 

Doctor.cs:

public class Doctor : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        animator = GetComponent<Animator>(); // komponensek lekérése

        m_navMeshAgent = GetComponent<NavMeshAgent>();

 

        m_soundHelp = Instantiate(m_soundHelp, this.transform);

        m_soundThankYou = Instantiate(m_soundThankYou, this.transform);

    }

 

    // Update is called once per frame

    void Update()

    {

        m_soundHelp.transform.position = this.transform.position; // hangok pozíciójának frissítése

        m_soundThankYou.transform.position = this.transform.position;

 

        if (null == sleep) // várakozás indítása (20 .. 30 másodperc között)

        {

            float fSleep = Random.Range(20.0f, 30.0f);

 

            sleep = Instantiate(m_objSleep);

            sleep.GetComponent<Sleep>().Begin(fSleep);

        }

        // play sound

        if (true == sleep?.GetComponent<Sleep>().IsEnd()) // "help" kiabálás indítása

        {

            Destroy(sleep);

            sleep = null;

 

            // sound

            if (Vector3.Distance(this.transform.position, m_Avatar.transform.position) > 25.0f && false == m_soundHelp.GetComponent<AudioSource>().isPlaying)

            {

                m_soundHelp.GetComponent<AudioSource>().Play();

            }

        }

 

        if (m_state == States.Waiting) // ha várakozik a doktor

        {

            animator.SetBool("walk", false); // várakozás animáció lejátszása

            animator.SetBool("idle", true);

 

            if (null == sleep)

            {

                float fSleep = Random.Range(20.0f, 30.0f);

 

                sleep = Instantiate(m_objSleep);

                sleep.GetComponent<Sleep>().Begin(fSleep);

            }

            // play sound

            if (true == sleep?.GetComponent<Sleep>().IsEnd()) // help kiabálás indítása 20 .. 30 másodperc között

            {

                Destroy(sleep);

                sleep = null;

 

                // sound

                if (false == m_soundHelp.GetComponent<AudioSource>().isPlaying)

                {

                    m_soundHelp.GetComponent<AudioSource>().Play();

                }

            }

 

            if (Vector3.Distance(this.transform.position, m_Avatar.transform.position) < 3.0f) // ha közel van az avatar a doktorhoz, akkor

            {

                if (true == m_soundHelp.GetComponent<AudioSource>().isPlaying) // help kiabálás leállítása

                {

                    m_soundHelp.GetComponent<AudioSource>().Stop();

                }

 

                // play sound

                m_soundThankYou.GetComponent<AudioSource>().Play(); // "thank you" hang lejátszása

                // logic

                DoctorsManager.Instance.Add(); // a DoctorsManager növelése eggyel

                // add heal

                Vector3 dir = (m_Avatar.transform.position - this.transform.position);

                dir.y = 0.0f;

                dir.Normalize();

                m_objHealUp = Instantiate<GameObject>(m_objHealUp, this.transform.position + (dir * 0.5f) + new Vector3(0, 0.7f, 0), Quaternion.Euler(0, 0, 0)); // HealUp lehelyezése az Doktor mellé

                m_objHealUp.SetActive(true);

 

                elapsedPos = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z);

                currentPos = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z);

 

                m_state = States.FollowToAvatar; // mostantól a doktor követi az avatar-t.

 

                if (null != sleep)

                {

                    Destroy(sleep);

                    sleep = null;

                }

            }

        }

        else if (m_state == States.FollowToAvatar)

        {

            // go to Avatar

            if (Vector3.Distance(this.transform.position, m_Avatar.transform.position) > 8.0f) // ha messze van a doktor az avatartól, akkor

            {

                m_navMeshAgent.stoppingDistance = 3.5f;

                m_navMeshAgent?.SetDestination(m_Avatar.transform.position); // útvonal keresése az avatarhoz

            }

 

            elapsedPos = currentPos;

            currentPos = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z);

            float velocity = Vector3.Distance(elapsedPos, currentPos) / Time.fixedDeltaTime;

 

            if (velocity < 0.05f) // ha kicsi a sebessége a doktornak, akkor várakozás animáció lejátszáse

            {

                animator.SetBool("walk", false);

                animator.SetBool("idle", true);

            }

            else // különben sétálás animáció lejátszása

            {

                animator.SetBool("idle", false);

                animator.SetBool("walk", true);

            }

        }

    }

 

    public enum States

    {

        Waiting,

        FollowToAvatar

    }

 

    Vector3 elapsedPos, currentPos;

 

    public States m_state = States.Waiting;

    GameObject sleep = null;

    Animator animator = null;

    NavMeshAgent m_navMeshAgent = null;

 

    [Header("Common")]

    [SerializeField] GameObject m_Avatar = null;

    [SerializeField] GameObject m_objSleep = null;

    [SerializeField] GameObject m_objHealUp = null;

    [Header("Sounds")]

    [SerializeField] GameObject m_soundHelp = null;

    [SerializeField] GameObject m_soundThankYou = null;

}

 

 

 

DoctorsManager.cs:

public class DoctorsManager : MonoBehaviour

{

    public static DoctorsManager Instance = null;

 

    private void Awake()

    {

        Instance = this; // eltárolni egy statikus változóban az egyetlen "DoctorsManager" címét.

    }

 

    // Start is called before the first frame update

    void Start()

    {

        m_nMaxDoctors = Instance.GetComponentsInChildren<Doctor>().Length;

        m_nNumDoctors = 0;

    }

 

    // Update is called once per frame

    void Update()

    {

        if (true == IsFailed()) // ha meghalt az avatar, akkor gameover scene betöltése

        {

            SceneManager.LoadScene("Scenes/GAMEOVER");

        }

 

        if (true == IsSuccess()) // ha megtalálta az összes doktort az avatar, és a helikopter közelében van mindenki, akkor a Success scene betöltése

        {

            SceneManager.LoadScene("Scenes/SUCCESS");

        }

    }

 

    public void Add() // egy doktort megtalált az avatar.

    {

        m_nNumDoctors++;

    }

 

    public int MaxDoctors() // doktorok maximális száma

    {

        return m_nMaxDoctors;

    }

 

    public int NumDoctors() // megtalált doktorok pillanatnyi értéke

    {

        return m_nNumDoctors;

    }

 

    public bool IsSuccess()

    {

        float fRadius = 15.0f;

 

        bool bIsOk1 = (m_nNumDoctors >= m_nMaxDoctors && Vector3.Distance(m_objAvatar.transform.position, m_objFinish.transform.position) < fRadius); // ha az avatar közel van a helikopterhez

 

        bool bIsOk2 = true;

        foreach (Doctor doctor in Instance.GetComponentsInChildren<Doctor>()) // ha az összes doktor közel van a helikopterhez

        {

            if (Vector3.Distance(doctor.gameObject.transform.position, m_objFinish.transform.position) > fRadius)

            {

                bIsOk2 = false;

            }

        }

 

        return (true == bIsOk1 && true == bIsOk2);

    }

 

    public bool IsFailed()

    {

        if (m_objAvatar.GetComponent<PlayerMovementScript>().GetLife() <= 0) // ha az avatar élete negatív

        {

            return true;

        }

 

        return false;

    }

 

    public bool IsFoundAllDoctors() // megtalálta az összes doktort az avatar?

    {

        if (m_nNumDoctors >= m_nMaxDoctors)

        {

            return true;

        }

 

        return false;

    }

 

    public float NextDoctorDistance() // legközelebbi várakozó doktor távolságát adja vissza ez a függvény

    {

        float fMinDistance = float.MaxValue;

 

        foreach (Doctor doctor in Instance.GetComponentsInChildren<Doctor>())

        {

            if (doctor.m_state == Doctor.States.Waiting)

            {

                float fDistance = Vector3.Distance(m_objAvatar.transform.position, doctor.gameObject.transform.position);

 

                if (fDistance < fMinDistance)

                {

                    fMinDistance = fDistance;

                }

            }

        }

 

        return fMinDistance;

    }

 

    public float FinishDistance() // a helikopter távolságát adja vissza ez a függvény

    {

        float fDistance = Vector3.Distance(m_objAvatar.transform.position, m_objFinish.transform.position);

        return fDistance;

    }

 

    [SerializeField] GameObject m_objAvatar = null;

    [SerializeField] GameObject m_objFinish = null;

    public int m_nMaxDoctors = 0;

    public int m_nNumDoctors = 0;

}

 

 

.